<!DOCTYPE html>
<html lang="en">
  <head>
    <title>圣诞树</title>
    <meta charset="utf-8" />
    <meta
      name="viewport"
      content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0"
    />
    <style>
      * {
        margin: 0;
        padding: 0;
      }

      html {
        overflow: hidden;
      }
    </style>
  </head>

  <body>
    <script type="module">
      import * as THREE from "./js/three.module.js";
      import { DDSLoader } from "./js/DDSLoader.js";
      import { MTLLoader } from "./js/MTLLoader.js";
      import { OBJLoader } from "./js/OBJLoader.js";

      var container, camera, scene, renderer, group, particleLight;
      var targetRotation = 0;
      var targetRotationOnMouseDown = 0;
      var mouseX = 0;
      var mouseXOnMouseDown = 0;
      var windowHalfX = window.innerWidth / 2;
      var windowHalfY = window.innerHeight / 2;

      init();
      animate();

      function init() {
        container = document.createElement("div");
        document.body.appendChild(container);

        scene = new THREE.Scene();
        // 加上场景雾
        scene.fog = new THREE.Fog(0xcce0ff, 500, 10000);

        // 设置镜头
        camera = new THREE.PerspectiveCamera(
          50,
          window.innerWidth / window.innerHeight,
          1,
          10000
        );
        // 初始视角
        camera.position.set(0, 100, 500);
        scene.add(camera);

        // 创建3D场景
        group = new THREE.Object3D();
        scene.add(group);
        // 铺设材料 start
        var imgTexture = THREE.ImageUtils.loadTexture("../img/texture.jpg");
        imgTexture.repeat.set(1, 1);
        imgTexture.wrapS = imgTexture.wrapT = THREE.RepeatWrapping;
        imgTexture.anisotropy = 16;
        imgTexture.needsUpdate = true;

        var shininess = 50,
          specular = 0x333333,
          bumpScale = 1,
          shading = THREE.SmoothShading;

        var materials = [];
        materials.push(
          new THREE.MeshPhongMaterial({
            map: imgTexture,
            bumpMap: imgTexture,
            bumpScale: bumpScale,
            color: 0xff0000,
            ambient: 0xffffff,
            specular: specular,
            shininess: shininess,
            shading: shading,
          })
        );
        materials.push(
          new THREE.MeshPhongMaterial({
            map: imgTexture,
            color: 0x008800,
            ambient: 0xffffff,
            specular: specular,
            shininess: shininess,
            shading: shading,
          })
        );
        materials.push(
          new THREE.MeshPhongMaterial({
            map: imgTexture,
            color: 0x584000,
            ambient: 0xffffff,
            shading: shading,
          })
        );
        materials.push(
          new THREE.MeshPhongMaterial({
            map: imgTexture,
            color: 0xff0000,
            ambient: 0xffffff,
            shading: shading,
          })
        );

        // 创建地面 start
        var groundColor = new THREE.Color(0xd2ddef);
        var groundTexture = THREE.ImageUtils.loadTexture(1, 2, groundColor);
        var groundMaterial = new THREE.MeshPhongMaterial({
          color: 0xffffff,
          specular: 0x111111,
          map: groundTexture,
        });

        var groundTexture = THREE.ImageUtils.loadTexture(
          "./img/ground.jpg",
          undefined,
          function () {
            groundMaterial.map = groundTexture;
          }
        );
        groundTexture.wrapS = groundTexture.wrapT = THREE.RepeatWrapping;
        groundTexture.repeat.set(25, 25);
        groundTexture.anisotropy = 16;

        var groundMesh = new THREE.Mesh(
          new THREE.PlaneGeometry(20000, 20000),
          groundMaterial
        );
        groundMesh.position.y = -150;
        groundMesh.rotation.x = -Math.PI / 2;
        group.add(groundMesh);

        // 创建雪花
        var sfMats = [];
        var sfTexture = THREE.ImageUtils.loadTexture("./img/snowflake.png");
        var sfGeometry = new THREE.Geometry();
        for (var i = 0; i < 15000; i++) {
          var vertex = new THREE.Vector3();
          vertex.x = Math.random() * 2000 - 1000;
          vertex.y = Math.random() * 2000 - 1000;
          vertex.z = Math.random() * 2000 - 1000;

          sfGeometry.vertices.push(vertex);
        }

        var states = [
          [[1.0, 0.2, 0.9], sfTexture, 10],
          [[0.9, 0.1, 0.5], sfTexture, 8],
          [[0.8, 0.05, 0.5], sfTexture, 5],
        ];
        for (var i = 0; i < states.length; i++) {
          var color = states[i][0];
          var sprite = states[i][1];
          var size = states[i][2];

          sfMats[i] = new THREE.ParticleSystemMaterial({
            size: size,
            map: sprite,
            blending: THREE.AdditiveBlending,
            depthTest: false,
            transparent: true,
          });
          sfMats[i].color.setHSL(color[0], color[1], color[2]);

          var particles = new THREE.ParticleSystem(sfGeometry, sfMats[i]);

          particles.rotation.x = Math.random() * 10;
          particles.rotation.y = Math.random() * 10;
          particles.rotation.z = Math.random() * 10;

          group.add(particles);
        }

        // 导入模型
        var onProgress = function (xhr) {
          if (xhr.lengthComputable) {
            var percentComplete = (xhr.loaded / xhr.total) * 100;
            console.log(Math.round(percentComplete, 2) + "% downloaded");
          }
        };

        var onError = function () {};

        var manager = new THREE.LoadingManager();
        manager.addHandler(/\.dds$/i, new DDSLoader());

        new MTLLoader(manager)
          .setPath("./mod/")
          .load("tree.mtl", function (materials) {
            materials.preload();
            new OBJLoader(manager)
              .setMaterials(materials)
              .setPath("./mod/")
              .load(
                "tree.obj",
                function (object) {
                  object.position.y = -95;
                  scene.add(object);
                },
                onProgress,
                onError
              );
          });

        // 创建光源

        // 创建自然光
        scene.add(new THREE.AmbientLight(0x222222));

        // 创建光粒子
        particleLight = new THREE.Mesh(
          new THREE.SphereGeometry(5, 10, 10),
          new THREE.MeshBasicMaterial({ color: 0xffffff })
        );
        particleLight.position.y = 250;
        group.add(particleLight);

        // 创建飞行粒子
        var pointLight = new THREE.PointLight(0xffffff, 1);
        camera.add(pointLight);
        scene.add(camera);

        // 增加定向光源
        var directionalLight = new THREE.DirectionalLight(0x0000ff, 0.5);
        directionalLight.position.set(10, 1, 1).normalize();
        group.add(directionalLight);

        // 开始渲染场景
        renderer = new THREE.WebGLRenderer({ antialias: true, alpha: false });
        renderer.setClearColor(scene.fog.color);

        renderer.setSize(window.innerWidth, window.innerHeight); // 宽高
        container.appendChild(renderer.domElement);

        renderer.setPixelRatio(window.devicePixelRatio);
        renderer.gammaInput = true;
        renderer.gammaOutput = true;
        renderer.physicallyBasedShading = true;

        // 添加事件
        document.addEventListener("mousedown", onDocumentMouseDown, false);
        document.addEventListener("touchstart", onDocumentTouchStart, false);
        document.addEventListener("touchmove", onDocumentTouchMove, false);
        window.addEventListener("resize", onWindowResize, false);
      }

      // 浏览器事件
      function onWindowResize() {
        windowHalfX = window.innerWidth / 2;
        windowHalfY = window.innerHeight / 2;
        camera.aspect = window.innerWidth / window.innerHeight;
        camera.updateProjectionMatrix();
        renderer.setSize(window.innerWidth, window.innerHeight);
      }

      function onDocumentMouseDown(event) {
        event.preventDefault();
        document.addEventListener("mousemove", onDocumentMouseMove, false);
        document.addEventListener("mouseup", onDocumentMouseUp, false);
        document.addEventListener("mouseout", onDocumentMouseOut, false);
        mouseXOnMouseDown = event.clientX - windowHalfX;
        targetRotationOnMouseDown = targetRotation;
      }

      function onDocumentMouseMove(event) {
        mouseX = event.clientX - windowHalfX;
        targetRotation =
          targetRotationOnMouseDown + (mouseX - mouseXOnMouseDown) * 0.02;
      }

      function onDocumentMouseUp(event) {
        document.removeEventListener("mousemove", onDocumentMouseMove, false);
        document.removeEventListener("mouseup", onDocumentMouseUp, false);
        document.removeEventListener("mouseout", onDocumentMouseOut, false);
      }

      function onDocumentMouseOut(event) {
        document.removeEventListener("mousemove", onDocumentMouseMove, false);
        document.removeEventListener("mouseup", onDocumentMouseUp, false);
        document.removeEventListener("mouseout", onDocumentMouseOut, false);
      }

      function onDocumentTouchStart(event) {
        if (event.touches.length == 1) {
          event.preventDefault();
          mouseXOnMouseDown = event.touches[0].pageX - windowHalfX;
          targetRotationOnMouseDown = targetRotation;
        }
      }

      function onDocumentTouchMove(event) {
        if (event.touches.length == 1) {
          event.preventDefault();
          mouseX = event.touches[0].pageX - windowHalfX;
          targetRotation =
            targetRotationOnMouseDown + (mouseX - mouseXOnMouseDown) * 0.02;
        }
      }
      // 动画
      function animate() {
        requestAnimationFrame(animate);
        render();
      }
      // 渲染
      function render() {
        var timer = Date.now() * 0.00025;
        group.rotation.y += (targetRotation - group.rotation.y) * 0.01;
        particleLight.position.x = Math.sin(timer * 7) * 300;
        particleLight.position.z = Math.cos(timer * 3) * 300;
        camera.position.x = Math.cos(timer) * 1000;
        camera.position.z = Math.sin(timer) * 500;
        camera.lookAt(scene.position);
        renderer.render(scene, camera);
      }
    </script>
  </body>
</html>
